#include <stdint.h>

#include "trap.h"
#include "console.h"
#include "syscall.h"
#include "proc.h"
#include "mmu.h"
#include "vm.h"
#include "string.h"

int
sys_exit()
{
    exit();
    return 0;
}

int
sys_yield()
{
    yield();
    return 0;
}

size_t sys_brk_impl(uint64_t addr) {
    struct proc *p = thisproc();
    // cprintf("sys_brk: p->sz=%llu, addr=%llu\n", p->sz, addr);

    if (addr > 0) {
        for (uint64_t va = p->sz; va < addr; va += PGSIZE - va % PGSIZE) {
            uint64_t *ptr;
            if ((ptr = pgdir_access(p->pgdir, va, 0)) == 0)
                return -1;

            *ptr |= PTE_USER_FLAGS;
        }

        p->sz = addr;
    }

    return p->sz;
}

size_t
sys_brk()
{
    uint64_t addr;
    if (argint(0, &addr) < 0)
        return -1;

    return sys_brk_impl(addr);
}

ssize_t sys_mmap() {
    uint64_t addr, len, prot, flags, fildes, off;
    if (argint(0, &addr) < 0 ||
        argint(1, &len) < 0 ||
        argint(2, &prot) < 0 ||
        argint(3, &flags) < 0 ||
        argint(4, &fildes) < 0 ||
        argint(5, &off) < 0)
        return -1;

    #define MAP_FIXED      0x10
    #define MAP_ANON       0x20

    if (!(flags & (MAP_ANON | MAP_FIXED)))
        return -1;
    if (fildes != -1 || off != 0)
        return -1;

    struct proc *p = thisproc();

    if (flags & MAP_FIXED) {
        if (len > PGSIZE)
            return -1;
        if (addr % PGSIZE != 0)
            return -1;

        char *page;
        uint64_t *ptr = pgdir_access(p->pgdir, addr, &page);
        if (!ptr)
            return -1;

        memset(page, 0, len);
        return addr;
    } else {
        addr = p->sz;
        if (sys_brk_impl(addr + len) != addr + len)  // FIXME: true mmap
            return -1;
        return addr;
    }
}

int
sys_clone()
{
    void *childstk;
    uint64_t flag;
    if (argint(0, (uint64_t*)&flag) < 0 || argint(1, (uint64_t*)&childstk) < 0)
        return -1;
    if (flag != 17) {
        cprintf("sys_clone: flags other than SIGCHLD are not supported.\n");
        return -1;
    }
    return fork();
}


int
sys_wait4()
{
    int64_t pid, opt;
    int *wstatus;
    void *rusage;
    if (argint(0, (uint64_t*)&pid) < 0 ||
        argint(1, (uint64_t*)&wstatus) < 0 ||
        argint(2, (uint64_t*)&opt) < 0 ||
        argint(3, (uint64_t*)&rusage) < 0)
        return -1;

    if (pid != -1 || wstatus != 0 || opt != 0 || rusage != 0) {
        cprintf("sys_wait4: unimplemented. pid %d, wstatus 0x%p, opt 0x%x, rusage 0x%p\n", pid, wstatus, opt, rusage);
        return -1;
    }

    return wait();
}
